#include "recparser.h"

recparser::recparser(QWidget *parent) :
        QWidget(parent)
{
    initLayout();

}

// Parsing routine
void recparser::dataparse(QByteArray inarray)
{
    if(!varVector.isEmpty())
    {
        // Cycle through the array contents
        for(int i=0;i<inarray.size();i++)
        {
            currentChar=inarray.at(i);
            switch(varVector.at(vecIndex).type)
            {
            case BYTE:
                // Fixed, matched, variable
                if(varVector.at(vecIndex).matched)
                {
                    QByteArray temparray;
                    if(parsearray.size()>0) temparray.append(parsearray);
                    temparray.append(currentChar);
                    QByteArrayMatcher matcher(temparray);
                    if(matcher.indexIn(varVector.at(vecIndex).matchArray,0)==0)
                    { // parsearray is still valid
                        parsearray.append(currentChar);
                        if(parsearray.size()>=varVector.at(vecIndex).matchArray.size())
                        {
                            acceptVariable();
                        }
                    }
                    else
                    {
                        testvarstruct->append(QString("matcher result: %1").arg(matcher.indexIn(varVector.at(vecIndex).matchArray,0)));;
                        rejectVector(QString("Received invalid byte: %1 in matched byte array").arg(currentChar));
                    }
                }
                else if(varVector.at(vecIndex).fixed)
                {
                    parsearray.append(currentChar);
                    if(parsearray.size()>=varVector.at(vecIndex).length)
                    {
                        acceptVariable();
                    }
                }
                else // Variable length
                {
                    // Check for matches or numbers
                    if(varVector.at(nextIndex()).type==BYTE)
                    {
                        if(currentChar==varVector.at(nextIndex()).matchArray.at(0))
                        {
                            acceptVariable();
                            i--;
                        }
                        else
                        {
                            parsearray.append(currentChar);
                            if(parsearray.size()>=MAXFIELDLENGTH)  // Array has reached max field length- move on & reject vector
                            {     QLabel *houseIcon = new QLabel(this);
                                houseIcon->setPixmap(QPixmap(":/images/house.png"));
                                houseIcon->move(20, 120);
                                houseIcon->show();
                                houseIcon->setAttribute(Qt::WA_DeleteOnClose);
                                rejectVector(QString("Reached maximum field length in a variable length byte field"));
                            }
                        }
                    }
                    else
                    { //number
                        if((currentChar>=48 && currentChar<=57) || (currentChar=='+' || (currentChar=='-' || currentChar=='.')))
                        {
                            acceptVariable();
                            i--;
                        }
                        else
                        {
                            parsearray.append(currentChar);
                            if(parsearray.size()>=MAXFIELDLENGTH)  // Array has reached max field length- move on & reject vector
                            {
                                rejectVector(QString("Reached maximum field length in a variable length byte field"));
                            }
                        }
                    }
                }
                break;
            case NUMBER:
                // Fixed or variable?
                if(varVector.at(vecIndex).fixed)
                {
                    if(currentChar>=48 && currentChar<=57) // Number:  we're cool with numbers.
                    {
                        spaceOK=false;
                        parsearray.append(currentChar);
                        if(parsearray.size()>=varVector.at(vecIndex).length)
                        {
                            acceptVariable();
                        }
                    }
                    else
                    {
                        switch(currentChar)
                        {
                        case ' ': // Accept UNLESS there are numbers in the array already- minus signs are OK
                            if(spaceOK)
                            {
                                parsearray.append(currentChar);
                                if(parsearray.size()>=varVector.at(vecIndex).length)
                                {
                                    acceptVariable();
                                }
                            }
                            else // Reject vector
                            {
                                rejectVector(QString("Received invalid space in a fixed number field"));
                            }
                            break;
                        case '.': // Accept UNLESS there is a period in the array already
                            if(periodOK)
                            {
                                parsearray.append(currentChar);
                                periodOK=false;
                                if(parsearray.size()>=varVector.at(vecIndex).length)
                                {
                                    acceptVariable();
                                }
                            }
                            else // Reject vector
                            {
                                rejectVector(QString("Received second period in a fixed number field"));
                            }
                            break;
                        case '-': // Accept UNLESS theres is a minus in the array already
                        case '+': // Accept UNLESS theres is a plus in the array already
                            if(signOK)
                            {
                                parsearray.append(currentChar);
                                signOK=false;
                                if(parsearray.size()>=varVector.at(vecIndex).length)
                                {
                                    acceptVariable();
                                }
                            }
                            else // reject Vector
                            {
                                rejectVector(QString("Received invalid plus sign in a fixed number field"));
                            }
                            break;
                        default:  // Anything else will break the vector!
                            rejectVector(QString("Received invalid byte: %1 in a fixed number field").arg(currentChar));
                            break;
                        }
                    }

                }
                else // Variable length
                {
                    if(currentChar>=48 && currentChar<=57) // Number:  we're always cool with numbers.
                    {
                        spaceOK=false;
                        parsearray.append(currentChar);
                        if(parsearray.size()>=MAXFIELDLENGTH)  // Array has reached max field length- move on & reject vector
                        {
                            rejectVector(QString("Reached maximum field length in a variable length number field with a number"));
                        }
                    }
                    else
                    {
                        switch(currentChar)
                        {
                        case ' ': // Accept UNLESS there are numbers in the array already- minus signs are OK
                            if(spaceOK)
                            {
                                parsearray.append(currentChar);
                                if(parsearray.size()>=MAXFIELDLENGTH)  // Array has reached max field length- move on- reject vector
                                {
                                    rejectVector(QString("Reached maximum field length in a variable length number field with a space"));
                                }
                            }
                            else // Move on to the next variable
                            {
                                acceptVariable();
                                // Space is not OK, but the next variable might be able to use it
                                // Only a byte is allowed to follow a variable number field-decrement index to reuse the same byte in the next for iteration
                                i--;
                            }
                            break;
                        case '.': // Accept UNLESS there is a period in the array already
                            if(periodOK)
                            {
                                parsearray.append(currentChar);
                                periodOK=false;
                            }
                            else
                            {
                                acceptVariable();
                                i--;
                            }
                            break;
                        case '-': // Accept UNLESS theres is a minus sign in the array already
                        case '+': // Accept UNLESS theres is a plus in the array already
                            if(signOK)
                            {
                                parsearray.append(currentChar);
                                signOK=false;
                            }
                            else
                            {
                                acceptVariable();
                                i--;
                            }
                            break;
                        default:  // Anything else will break the vector!
                            acceptVariable();
                            i--;
                            break;
                        }
                    }

                }
                break;
            default: // Unhandled
                break;
            }
        }
    }
}

void recparser::acceptVariable()
{
    if(varVector[vecIndex].type==NUMBER)
    {
        // Eliminate spaces between minus sign and first number
        unsigned int i=0;
        unsigned int arraysize=parsearray.size();
        while(i<arraysize)
        {
            if(parsearray.at(i)==' ')
            {
                parsearray.remove(i,1);
                arraysize=parsearray.size();
                i--;
            }
            i++;
        }

        varVector[vecIndex].floatvalue=parsearray.toDouble();
        testvarstruct->append(QString("%1").arg(varVector[vecIndex].floatvalue));
    }
    else // BYTE
    {
        if(varVector[vecIndex].matched)
        {
            testvarstruct->append(varVector[vecIndex].matchArray);
        }
        else
        { // For fixed- and variable-length byte arrays just dump whatever we've got in the matchArray
            varVector[vecIndex].matchArray.clear();
            varVector[vecIndex].matchArray.append(parsearray);
            testvarstruct->append(varVector[vecIndex].matchArray);
        }
    }

    parsearray.clear();
    vecIndex++;
    spaceOK=true;
    signOK=true;
    periodOK=true;
    if(vecIndex==varVector.size()) // Reached end of vector
    {
        vecIndex=0;
        validVector=true;
        emit(vectorout(varVector,validVector));
    }
}

void recparser::rejectVector(QString cause)
{
    parsearray.clear();
    testvarstruct->append(cause);
    vecIndex=0;
    periodOK=true;
    spaceOK=true;
    signOK=true;
    validVector=false;
    emit(vectorout(varVector,validVector));
}

int recparser::nextIndex()
{
    if((vecIndex+1)>=varVector.size())
        return 0;
    else return vecIndex+1;
}

void recparser::savePackage()
{
    varVector.clear();
    testvarstruct->clear();

    for(int i=0;i<variablelist->count();i++)
    {
        QByteArray temp;
        variable tempvar;
        tempvar.matched=false;
        tempvar.fixed=false;
        tempvar.length=0;
        QListWidgetItem *item = variablelist->item(i);
        temp.append(item->text());


        switch(temp.at(1))
        {
        case '#':
            tempvar.fixed=true;
            break;
        case '=':
            tempvar.matched=true;
            break;
        default:  // *, non-fixed length
            break;
        }

        switch(temp.at(0))
        {
        case 'c': // Byte array
            tempvar.type=BYTE;
            break;
        case 'n': // Number
            tempvar.type=NUMBER;
            break;
        default:  // Unhandled
            break;
        }

        if(tempvar.matched) // Matched byte array
        {
            tempvar.matchArray.clear();
            for(int j=2;j<temp.size();j++)
            {
                tempvar.matchArray.append(temp.at(j));
            }
        }
        else if(tempvar.fixed) // Fixed length
        {
            int templength=0;
            for(int j=2;j<temp.size();j++)
            {
                templength+=temp.at(j)-48;
            }
            tempvar.length=templength;
        }
        else // Variable length
        {

        }
        varVector.append(tempvar);
    }

    // Check that for every variable-length byte array there is a number after it, or a matched byte array
    for(int j=0;j<varVector.size();j++)
    {
        if(varVector.at(j).type==NUMBER)
        {
            varVector[j].floatvalue=0;
        }
        if(varVector.at(j).type==BYTE && !(varVector.at(j).fixed || varVector.at(j).matched))
        { // Variable length array, not matched
            if(j<varVector.size()-1) // Still have variables left
            {
                if(!(varVector.at(j+1).matched || varVector.at(j+1).type==NUMBER))  // Still can handle this scenario
                {
                    QString testout;
                    testout.append("A number or matched-byte array must follow a variable-length byte array");
                    testvarstruct->append(testout);
                    varVector.clear();
                    return;
                }
            }
            else // end of the vector
            {
                if(!(varVector.at(0).matched || varVector.at(0).type==NUMBER))
                {
                    QString testout;
                    testout.append("The data stream cannot end with a variable-length byte array if the starting variable is not a number or matched byte array ");
                    testvarstruct->append(testout);
                    varVector.clear();
                    return;
                }
            }
        }
        if(varVector.at(j).type==NUMBER && !(varVector.at(j).fixed || varVector.at(j).matched))  // do not put another number variable right after a variable-length number
        {
            if(j<varVector.size()-1) // Still have variables left
            {
                if(varVector.at(j+1).type!=BYTE) // next variable is a byte
                {
                    QString testout;
                    testout.append("The data stream cannot contain a number variable preceded by a variable-length number field");
                    testvarstruct->append(testout);
                    varVector.clear();
                    return;
                }
            }
            else
            {
                if(varVector.at(0).type!=BYTE)  // no variables left, check first variable for byte type
                {
                    QString testout;
                    testout.append("The data stream cannot end with a variable-length number field if the starting variable is not byte array");
                    testvarstruct->append(testout);
                    varVector.clear();
                    return;
                }
            }

        }
    }

    curstate=START;
    pastdecimal=false;
    decimalPlace=1;
    parsing=false;

}

void recparser::addVariable()
{
    int fieldlength;
    QByteArray arraymatch;
    QByteArray varout;
    varout.clear();
    arraymatch.clear();
    switch(this->variabletype->currentIndex())
    {
    case 0: // Byte
        // Check for fixed length
        if(fixed_length_check->isChecked())
        {
            // Check length
            fieldlength=length_spin->value();
            // Check matching array
            if(byte_match_check->isChecked())
            {
                varout.append("c=");
                arraymatch.append(byte_array_edit->text());
                arraymatch.truncate(fieldlength);
                varout.append(arraymatch);
                this->variablelist->addItem(varout);
                QListWidgetItem *item = new QListWidgetItem;
                item->setIcon(QIcon("./icons/abc.svg"));
                listview->insertItem(0,item);


            }
            else
            {
                varout.append("c#");
                varout.append(QString("%1").arg(fieldlength));
                QStandardItem *item = new QStandardItem;
                item->setIcon(QIcon("./icons/num.svg"));
                this->variablelist->addItem(varout);
            }
        }
        else // Variable array byte
        {
            //Check matching array
            if(byte_match_check->isChecked())
            {
                varout.append("c=");
                arraymatch.append(byte_array_edit->text());
                if(!arraymatch.isEmpty())
                {
                    varout.append(arraymatch);
                    this->variablelist->addItem(varout);
                }
            }
            else
            {
                this->variablelist->addItem(QString("c*"));
            }
        }
        break;
    case 1: // Number
        // Check for fixed length
        if(fixed_length_check->isChecked())
        {
            // Check length
            fieldlength=length_spin->value();

            varout.append("n#");
            varout.append(QString("%1").arg(fieldlength));
            this->variablelist->addItem(varout);
        }
        else
        {
            // Variable-length byte
            this->variablelist->addItem(QString("n*"));
        }
        break;
    default:
        break;
    }
}

void recparser::removeVariable()
{
    delete variablelist->currentItem();
}

void recparser::testVector()
{


}

void recparser::initLayout()
{
    dataout.clear();
    variablelist = new QListWidget;
    mainLayout = new QHBoxLayout;
    ctlButtons = new QVBoxLayout;
    settingsLayout = new QVBoxLayout;
    fixedlengthLayout = new QHBoxLayout;
    bytematchLayout = new QHBoxLayout;
    variabletype = new QComboBox;
    fixed_length_check = new QCheckBox("Fixed length");
    length_spin = new QSpinBox;
    byte_array_edit = new QLineEdit;
    byte_match_check = new QCheckBox("Match");
    add_removeLayout = new QVBoxLayout;
    add_item_button = new QPushButton("Add");
    remove_item_button = new QPushButton("Remove");
    save_button = new QPushButton("Save");
    testvarstruct = new QTextEdit;
    testvarstruct->setDisabled(true);
    testvarstruct->setMaximumHeight(40);
    abcButton = new QPushButton;
    abcButton->setIcon(QIcon("./icons/abc.svg"));
    abcButton->setIconSize(QSize(50,50));
    abcButton->setFixedWidth(60);
    numButton = new QPushButton;
    numButton->setIcon(QIcon("./icons/num.svg"));
    numButton->setIconSize(QSize(50,50));
    numButton->setFixedWidth(60);
    model = new QStandardItemModel;
    modellist = new QList<QStandardItem>;
    listview = new QListWidget;
    listview->setFlow(QListView::LeftToRight);
    listview->setFixedHeight(100);
    listview->setViewMode(QListView::IconMode);
    listview->setMovement(QListView::Static);
    length_spin->setMaximum(1024);
    length_spin->setMinimum(1);
    length_spin->setValue(4);
    variabletype->addItem("Bytes");
    variabletype->addItem("Number");

    fixedlengthLayout->addWidget(fixed_length_check);
    fixedlengthLayout->addWidget(length_spin);
    bytematchLayout->addWidget(byte_match_check);
    bytematchLayout->addWidget(byte_array_edit);
    settingsLayout->addWidget(listview);
    settingsLayout->addWidget(variabletype);
  //  settingsLayout->addLayout(fixedlengthLayout);
  //  settingsLayout->addLayout(bytematchLayout);
    add_removeLayout->addWidget(add_item_button);
    ctlButtons->addWidget(abcButton);
    ctlButtons->addWidget(numButton);
    add_removeLayout->addWidget(remove_item_button);
    add_removeLayout->addWidget(save_button);

    //mainLayout->addLayout(settingsLayout);
    mainLayout->addLayout(ctlButtons);
    mainLayout->addStretch(1);
  //  mainLayout->addLayout(add_removeLayout);
    mainLayout->addLayout(settingsLayout);
    mainLayout->addStretch(1);
    mainLayout->addWidget(variablelist);
    mainLayout->addWidget(testvarstruct);

    this->setLayout(mainLayout);
    this->setMinimumHeight(300);
    this->setMinimumWidth(700);
    connect(add_item_button,SIGNAL(clicked()),this,SLOT(addVariable()));
    connect(remove_item_button,SIGNAL(clicked()),this,SLOT(removeVariable()));
    connect(save_button,SIGNAL(clicked()),this,SLOT(savePackage()));
    curstate=START;
    vecIndex=0;
    parsearray.clear();
    spaceOK=true;
    signOK=true;
    periodOK=true;
}


